(function(){

/**
 * 把选择器或者dom封装成Hquery函数对象，这个函数对象在下面被定义着
 * @param sel String/DOM 如果是字符串，则只接受以'#'、'.'以及无符号开头的单个选择器。如果是DOM，直接把这个DOM放到Hquery.el[0]这个位置
 */
function Z(sel){
	return (new Hquery(sel));
}

function Hquery(sel){
	this.el = typeof sel=='string'?this._selectEl(sel):(H.isArray(sel)?sel:(sel instanceof Hquery?sel.el:(sel?[sel]:[])));
}

var _h = Hquery.prototype;

/**
 * 选择元素
 * @param elName String 选择器，只能是单个选择器
 * @param parent 要选择的元素的父级，只能是单个的DOM对象
 */
_h._selectEl = function(elName,parent){
	if(parent&&parent instanceof Array){console.error("[Hquery]父级应该是单个")}
	var el,parent = parent || document;
	if(typeof elName=="string"){
		var sel=this.judgeSelector(elName),selChar=elName.slice(1);
		switch(sel){
			case "ID":
				el=[parent.getElementById(selChar)];
				if(!el[0]){el=[]}//因为getElementById为null时，el会是[null]
				break;
			case "tagName":
				el=parent.getElementsByTagName(elName);
				break;
			case "className":
				el=parent.getElementsByClassName(selChar);
				break;
			default:
				el=[];
		}
	}
	else el=elName;
	
	return el;
}

/**
 * 选择s
 */

/**
 * 查找指定的祖节点
 * @param sel String 单个选择器
 * @param sel function 当为函数时，根据此函数的return为true or false 来确定这个父级是否为目标。此函数有两个参数，1是需要判断的原来dom的父级，2是原dom
 * @param one boolean 是否第一个匹配到了就不再找别的了(是每个元素匹配到一个父元素的意思，不是总共只匹配一个的意思)
 */
_h.parents = function(sel,one){
	var newEls = [],
		parentNode,
        selFn = typeof sel=='function'?sel:null,
		targetSel = selFn?'function':this.judgeSelector(sel),//是什么选择器
		selChar = selFn?'':sel.slice(1),//去掉首字母的选择器
		judge = false;

    this.foreach(function(tar){
        if(tar==document)return;
        parentNode = tar.parentNode;
        while(parentNode!=document){
            //判断是否正确
            switch(targetSel){
                case "tagName":
                    judge = parentNode.tagName==sel.toUpperCase()
                    break;
                case 'className':
                    judge = this.hasClass(selChar,parentNode);
                    break;
                case 'function':
                    judge = selFn(parentNode,tar);
                    break;
            }

            if(judge){
                newEls.push(parentNode);
                if(one)
                    break;
            }

            parentNode = parentNode.parentNode;
        }
    });


	return Z(newEls);
};

/**
 * 返回所有元素的直接父节点
 * @param fn =null function 如果有此参数，则遍历所有级别的父元素，然后依次作为参数调用此方法，根据此方法的返回值判断此元素是否是需要的父元素，此函数需返回true/false
 */
_h.parent = function(fn){
    var parents = [];
    this.foreach(function(tar){
        parents.push(tar.parentNode);
    });
	return Z(parents);
};

/**
 * 根据选择器返回元素的同级元素
 * @param sel String/number 如果为number，则只接受4个值 1:下一个,-1:上一个,2:最后一个,-2:第一个
 *							如果为空，返回所有同级元素
 */
_h.borther = function(sel){
	var els = this.el,
		result=[];
    this.foreach(function(tar){
        if(sel===1){
            result.push(els[0].nextElementSibling||els[0].nextSibling);
        }else if(typeof sel=='string'){
            result = result.concat(Z(tar).parent().children(sel,true).el);
        }else{
            console.error('[Hquery]功能还没写好咧');
        }
    });
	
	return Z(result);
};

/**
 * 查找所有父级的所有指定的子节点
 * @param sele 选择器，字符串类型，此为false时，返回this.el中全部元素的所有直接子元素
 * @param near boolean 是否只选择直接子节点
 */
_h.children = function(sele,near){
	var els = this.el,
		newEls = [],
		eNum = els.length,
		selectEls=[];
	if(!eNum)return this;
	for(var i=0;i<eNum;i++){//匹配选择器里的所有元素的子元素
		if(near||!sele)//返回直接子元素
			selectEls = els[i].children;
		else
			selectEls = this._selectEl(sele,els[i]);
		Array.prototype.push.apply(newEls,selectEls);//_selectEl返回的是数组，所以要用这种方式添加元素
	}
	
	var newHq = Z(newEls);
	
	if(near&&sele)//这时候才筛掉子元素里不符合选择器的元素
        newHq = newHq.filterEl(sele);
	
	return newHq
};

/**
 * 判断单个选择器使用的是什么选择器
 */
_h.judgeSelector = function(elName){
	return {'#':'ID','.':'className'}[elName.charAt(0)] || "tagName"
};

/**
 * 筛选this.el中的所有el，得出的this.el可为多个
 * @param sel String  单个选择器
 */
_h.filterEl = function(sel){
	var selType=this.judgeSelector(sel),
		selChar=sel.slice(1),
		els = this.el,
		newEls = [],
		eNum = els.length,
		i = 0;
	switch(selType){
		case "ID":
			break;
		case "tagName":
			for(;i<eNum;i++){
				if(els[i].tagName.toLowerCase()==sel.toLowerCase()){
					newEls.push(els[i]);
				}
			}
			break;
		case "className":
			for(;i<eNum;i++){
				if(this.hasClass(selChar,els[i])){
					newEls.push(els[i]);
				}
			}
			break;
	}
	
	return Z(newEls)
};

/**
 * 移除元素内为空的节点，遍历子元素的时候有用？（在chrome中，换行，文本，注释也算作一个子节点）
 * 
 */
_h.clearChildren = function(tar){
	if(tar)
        clearChild(tar);
    else
        this.foreach(clearChild);

    function clearChild(tar){
        var children = tar.childNodes,cNum = children.length,i=0;
        for(;i<cNum;i++){
            if(children[i]&&(children[i].nodeType===3||children[i].nodeType===8)){
                tar.removeChild(children[i]);
            }
        }
    }
    return this;
};

/**
 * 选出所有后代项
 * */
_h.allChildren = function(){
    var allChild = [],loading = [];
    this.foreach(function(tar){
        putNewChild(tar);
        while(loading.length){
            putNewChild(loading.shift());
        }
    });

    //把tar的子项放进allChild和loading中
    function putNewChild(tar){
        if(!tar)return;
        var child = tar.children;
        if(!child)return;

        child = Array.prototype.slice.call(child,0);
        allChild = allChild.concat(child);
        loading = loading.concat(child);
    }

    return Z(allChild);
};

/**
 * 遍历选中的每个元素
 * @param fn Function 回调函数，回调函数中this表示此query，第一个参数表示正在处理的DOM
 */
_h.foreach = function(fn){
	var els = this.el,
		eNum = els.length,
		i=0,
		result;
	if(!els[0])return this;
	for(;i<eNum;i++){
		result = fn.apply(this,[els[i],i]);
		if(result=='break')
			break;
	}
};



/**=========================================================
 * 样式和class，属性
 * =========================================================*/

 /**
 * 移除class
 * @param cName String 要移除的class名
 * @param tar 单个Dom对象，是移除这个对象的class
 */
_h.removeClass = function(cName,tar){
	var els = tar?[tar]:this.el,
		nums = els.length,
		i = 0;
	for(;i<nums;i++){
		var el = els[i];
		el.className = H.trim(el.className.replace(RegExp(cName),""));
	}
	return this
};
/**
 * 添加class
 * 现代浏览器的class的操作参考http://www.zhangxinxu.com/wordpress/2013/07/domtokenlist-html5-dom-classlist-%E7%B1%BB%E5%90%8D/
 * @param cName String 要移除的class名
 * @param tar=null DOM 单个Dom对象，是移除这个对象的class
 */
_h.addClass = function(cName,tar){
	var els = tar?[tar]:this.el,
		eNum = els.length,
		i = 0;
	for(;i<eNum;i++){
		var el = els[i];
		if(this.hasClass(cName,el))continue
		el.className = el.className+" "+cName;
	}
	return this
};
/**
 * 判断单个元素是否包含某class
 * @param cName String 要判断的class名，如果为空，则返回元素的所有class数组
 * @param el=null DOM 单个Dom对象，要判断的元素
 * @return 如果指定了某个cName，则返回true/false，否则，返回包含所有class的一个数组
 */
_h.hasClass = function(cName,el){
	el = el || this.el[0];
	return cName?(el.className.search(RegExp(cName))>=0?'cName':false):el.className.split(' ')
};
/**
 * @param cName string class名
 * */
_h.toggleClass = function(cName){
    this.hasClass(cName)?this.removeClass(cName):this.addClass(cName);
    return this
};
/**
 * 直接修改样式
 * @param name String 与css样式中的写法一致的
 * @param value String 与css样式中的写法一致
 * @param tar DOM 单个Dom对象
 */
_h.css = function(name,value,tar){
	var els = tar?[tar]:this.el,
		eNum = els.length || 0;
	if(!els[0])return this;

    var cssObj={};
    if(typeof name=='string'){
        if(!value&&value!==0){
            var cssText = els[0].style.cssText.match(new RegExp(name+'[^;]*;','i'));

            return cssText?H.trim(cssText[0].split(':')[1]):false;
        }
        cssObj[name] = value;
    }else if(H.isArray(name)){
        var returnObj = {},i= 0,nNum = name.length;
        for(;i<nNum;i++){
            returnObj[name[i]] = els[0].style.cssText.match(new RegExp(name+'[^;]*;','i'))[0];
        }
        return returnObj;
    }else if(typeof name=='object'){//设置多对css
        cssObj = name;
    }

	for(var i=0;i<eNum;i++){
		var oldCss = els[i].style.cssText,
			newCss = "";
        for(var ii in cssObj){
            if(oldCss){//这个cssText是否已经填写过内容了
                /*var oldCssArr = H.trim(oldCss).split(';'),
                 oNum = oldCssArr.length,
                 ii = 0;
                 for(;ii<oNum;ii++){
                 var oneCss = H.trim(oldCssArr[ii]);
                 if(oneCss===""||oneCss.search(RegExp(name))>=0)continue;//这两种情况下不把这个单个的css加回来了
                 newCss += oneCss+";";
                 }*/
                newCss = oldCss.replace(new RegExp(ii+'[^;]*;','g'),'')||'';
            }
            newCss += ii+':'+cssObj[ii]+';';
        }

		els[i].style.cssText = newCss;
	}
	return this
};
/**
 * 移除写在style属性上的css
 * @param name String 要移除的css的名字
 * @param tar 要移除的DOM 节点，默认是this.el
 */
_h.removeCss = function(name,tar){
	if(!name)return;
    var oldCss;
    this.foreach(function(tar){
        oldCss = tar.style.cssText;
        tar.style.cssText = oldCss.replace(new RegExp(name+'[^;]*;','g'),'');
    });
    return this
};
/**
 * 切换css
 * @param name String 与css样式中的写法一致的
 * @param value String 与css样式中的写法一致
 * */
_h.toggleCss = function(name,value){
    if(!value)console.error('[Hquery]参数不够啊');
    if(this.css(name))
        this.removeCss(name);
    else
        this.css(name,value);
};

_h.show = function(){
	this.removeCss('display');
	return this;
};
_h.hide = function(){
	this.css('display','none');
	return this;
};

_h.toggleDisplay = function(){
    if(this.compCss('display')=='none'){
        this.show();
    }else{
        this.hide();
    }
};

/**
 * 修改、返回属性
 * @param name String 要修改或者返回的属性名，对大小写不敏感
 */
_h.attr = function(name,value){
	var el = this.el[0];
	if(!el)return;
	var attrs = el.attributes;
	if(!name){//如果没有输入name，则返回所有属性
		var aNum = attrs.length,
			i=0,
			attrObj = {};
		for(;i<aNum;i++){
			var attrName = attrs[i].name||attrs[i].nodeName||attrs[i].localName,
				attrValue = attrs[i].value||attrs[i].nodeValue||attrs[i].textContent||'';
			attrObj[attrName] = attrValue;
		}
		return attrObj
	}
	
	name = name.toLowerCase();
	if(!value){
		if(!el.attributes[name])return false;//如果没有这个属性
		return el.attributes[name].value;
	}else{
		if(el.attributes[name])attrs[name].value = value;
		else{//如果没有这个属性
			var attr = document.createAttribute(name);
			attr.value = value;
			attrs.setNamedItem(attr);
		}
		return this
	}
};

/**
 * 兼容性高的返回元素的信息，仅支持return内的内容
 * @param item String 要返回的内容
 * @param tar DOM
 */
_h.inf = function(item,tar){
	var els = tar?[tar]:this.el,
		el = els[0],
		val;
	if(!el)return;
	switch(item){
		case 'height':
			val = el.height || el.offsetHeight || el.clientHeight;
			break;
		case 'width':
			val = el.width || el.offsetWidth || el.clientWidth;
			break;
		case 'value':
			val = H.trim(el.value);
			break;
		case 'checked'://针对多选和单选按钮的
			val = el.checked;
			break;
		case 'text'://Dom对象的文本
			val = H.trim(el.innerText);
			break;
		case 'selectText'://针对select对象的
			val = H.trim(el[el.selectedIndex].innerText);
			break;
	}
	return val;
};

/**
 * 返回计算后的信息
 * @param item string/array
 * @return array 第一个元素的css是[0]
 * */
_h.compCss = function(item){
    var infs = [],
        inf = {},
        allInf = {},
        formatInf = [];
    if(item){
        var items = typeof item=='string'?[item]:item,
            iNum = items.length;
    }
    this.foreach(function(tar){
        allInf = H.getComputedStyle(tar);
        if(item)
            for(var i=0;i<iNum;i++){
                inf[items[i]] = allInf[items[i]];
            }
        else
            inf = allInf;
        infs.push(inf);
    });

    formatInf = infs;
    if(item&&items.length==1){
        for(var i=0;i<formatInf.length;i++){
            formatInf[i] = formatInf[i][item];
        }
    }

    return formatInf.length==1?formatInf[0]:formatInf;
};
/**=========================================================
 * DOM添加删除等操作
 * =========================================================*/
/**
 * 把字符串型的DOM 添加到选择器中元素的位置（具体位置可选）
 * @param str String 要添加的字符串
 * @param clear=false boolean 是否要清除原html内容(可忽略此参数，直接写第三个参数)
 * @param appTo='beforeEnd' String 为 insertAdjacentHTML 的第一个参数
						1.beforeBegin: 插入到标签开始标记前
						2.afterBegin:  插入到标签开始标记后
						3.beforeEnd:   插入到标签结束标记前
						4.afterEnd:    插入到标签结束标记后
 */
_h.appHTML = function(str,clear,appTo){
	var els = this.el,
		eNum = els.length,
		clearIsString = (typeof clear=='string'),
		i=0,
		el;

    appTo = appTo || (clearIsString?clear:'beforeEnd');
    clear = clearIsString?false:clear;
	if(!eNum)return false;
	for(;i<eNum;i++){
		el=els[i];
		clear && (el.innerHTML = '');
		el.insertAdjacentHTML(appTo,str);
	}
	return this
};
/**
 * 把html节点（dom对象）添加到选择器中元素的子节点数组（children）中
 * @param node DOM（node） html节点（dom对象）
 * @param 和appHTML的一致
 */
_h.appNode = function(node,clear,appTo){
	var els = this.el,
		eNum = els.length,
		nodes = H.isArray(node)?node:[node],
		nNum = nodes.length,
		appTo = appTo || 'beforeEnd',
		i=0,
		el;
	if(!eNum)return false;
	for(;i<eNum;i++){
		el=els[i];
		clear && (el.innerHTML = '');
		for(var ii=0;ii<nNum;ii++){
			if(appTo=='beforeEnd')el.appendChild(nodes[ii]);
			else if(appTo=='afterBegin')el.insertBefore(nodes[ii],el.childNodes[0]);
			else if(appTo=='beforeBegin'){}//剩下的两个比较少用，先不写
		}
	}
	return this
};

/**
 * 移除dom节点
 * */
_h.remove = function(){
    this.foreach(function(tar){
        tar.remove();
        //tar.parentNode.removeChild(tar);
    })
};

/**=========================================================
 * 事件s
 * =========================================================*/
/**
 * 事件
 * @param ev string 要绑定的事件
 * @param sel string 如果有这个参数，则为事件委托，事件绑定到this.el，但是实际响应事件的是它的children(sel)中的选择的元素
 * @param fn function 事件发生时的回调函数
 * @param ti number 事件要发生几次就自动解绑，默认0不会解绑事件，1为1次，目前只支持1次和不解绑。
 boolean 作为addEventListener的第三个参数直接赋予
 * @callback
 *		this : 这个类
 *		param1:触发事件的元素dom
 *  	param2:事件对象
 *		param3:移除这个事件的函数，调用这个函数就可以移除事件了，如果要指定移除某个元素的相关事件，要把元素从参数传进来
 */
_h.on = function(ev,sel,fn,ti){
    var obj = this,
        el = obj.el,
        eNum = el.length,
        useCapture = true;
    if(sel&&typeof sel!='string'){
        fn = sel;
        ti = fn;
        sel = false;
    }
    ti = typeof ti=='number'?ti:0;
    if(!eNum)return this;

    /**
     * 由于可能需要在事件未执行时就移除事件，所以需要这个off函数
     * 参数与on一致
     */
    _h.off = function(){
        var el = obj.el,
            eNum = el.length;
        if(!eNum)return this;

        for(var i=0;i<eNum;i++){
            el[i].removeEventListener(ev,add,useCapture||false);//
        }
        console.log('off');
    };

    function add(e){
        if(sel){
            //e.target不一定等于this，而是触发该事件的最小dom，所以有可能是目标dom的子dom
            var els = obj.children(sel),
                eTar = e.target;

            if(H.inArray(eTar,els.el)){
                fn.apply(obj,[eTar,e,remove]);
            }else{
                Z(eTar).parents(function(par){
                    if(H.inArray(par,els.el)){
                        fn.apply(obj,[par,e,remove]);
                        return true;
                    }
                    return false;
                },true);
            }
        }else{
            fn.apply(obj,[this,e,remove]);
        }
        //e.preventDefault();//阻止默认
        //e.stopPropagation();//阻止事件传播
        ti && remove(this);
    }
    /**
     * 在回调函数中作为参数返回的函数，可以移除该事件。
     * 这个方法有不完善的地方，如果用on给同一个元素添加了多次同样的事件，即使fn不一样，也会被删除，
     * 这是因为移除事件所用的fn不是外部的，而是内部的add函数
     */
    function remove(tar){
        var els = tar?[tar]:el,
            eNum = els.length;
        for(var i=0;i<eNum;i++){
            els[i].removeEventListener(ev,add,useCapture);//移除时，第三个参数要和addEventListener一致，默认均为false
        }
        //console.log(add);
    }

    for(var i=0;i<eNum;i++){
        el[i].addEventListener(ev,add,useCapture);
    }
    return remove;
};


/**
 * 滚动到底部的事件
 * @param fn Function 回调函数
 * @param faultTolerant=18 number 容错范围
 */
_h.scrollBottom = function(fn,faultTolerant){
    var Hq = this,removes=[];
    faultTolerant = faultTolerant || 18;
    this.on('scroll',add);

    function add(a,b,remove){
        if(a.scrollHeight<=a.scrollTop+Hq.inf('height',a)+faultTolerant){
            removes.push(remove);
            fn.apply(this,[a,b,removeAll]);
        }
    }
    //移除scroll和load响应的侦听
    function removeAll(tar){
        //Hq.off();return;
        var rNum = removes.length;
        for(var i=0;i<rNum;i++){
            removes[i](tar);
        }
    }

    var tarDom = this.el[0];
    if(tarDom.scrollHeight<=tarDom.scrollTop+Hq.inf('height')+faultTolerant){
        fn.apply(this,[this,null,removeAll]);
    }

    return this;
};


//把 h/Z/z 注册到 window 下
if(typeof define === 'function' && typeof define.amd === 'object' && define.amd){
    // AMD. Register as an anonymous module.
    define(function(){
        return Z;
    });
}else if(typeof module !== 'undefined' && module.exports){
    module.exports = Z;
    module.exports.Z = Z;
}else{
    window.Z = Z;
    window.z = Z;
    window.h = Z;
    window.Hquery = Hquery;
}

})();


/**=========================================================
 * 小工具
 * =========================================================*/
var H = new function(){

    /**
     * 四舍五入到小数多少位
     * @param int int 原数值
     * @param i number 精确到多少位，如果为负数，则会返回科学计数法的string
     * */
    this.round = function(int,i){
        if(i>=0){
            var d = i*10;
            return Math.round(int*d)/d;
        }else
            return int;
    };
    /**
     * 计算
     * */
    this.plus = function(a){
        var e = 0;
        if($.isArray(a))
            for(var i=0;i< a.length;i++)
                e+=a[i];
        return e;
    };

    /**
     * 替换url中的queryString的值
     * @param url string
     * @param k object|string 这个可以是键值对的对象，也可以只是key名
     * @param v
     * */
    this.urlReplaceArg = function(url,k,v){
        if(typeof url!='string')return url;
        if(typeof k!='object'){
            var kk = {};
            kk[k] = v;
            k = kk;
        }
        for(var i in k){
            if(k.hasOwnProperty(i)){
                var reg = new RegExp('([&?]'+i+'\\=)[^?&]*','g');//两个右斜杠，在变为reg后会变成一个的
                if(url.search(reg)>=0)
                    url = url.replace&&url.replace(reg,'$1'+k[i]);//这是只匹配(?&)和其后面的值，匹配出来的$1是?&之一。原字符串未被正则匹配的部分是不会变的，replace之后只改匹配成功的部分，而不是只生成后面表达式的部分
                else
                    url = url+(url.indexOf('?')>=0?'&':'?')+i+'='+k[i];
            }
        }
        return url;
    };
	this.trim = function(str){
        if(typeof str=='string')
		    return str.replace(/(^\s+)|(\s+$)/g,'');
        else if(str instanceof HTMLElement&&str.childNodes){
            var childrens = str.childNodes,i=0;
            for(;i<childrens.length;){
                if(childrens[i].nodeType==3){
                    str.removeChild(childrens[i]);
                }else
                    i++;
            }
        }else{
            return str;
        }
	};
	
	this.isTouchDevice = function(){
		return ('ontouchstart' in document.documentElement);
	};
	
	/**
	 * 打包好的，兼容的，获取各种信息的方法
	 * @param name String 要获取的是什么信息
	 */
	this.inf = function(name){
		var obj;
		switch(name){
			case 'windowHeight':
				obj = window.innerHeight || document.body.clientHeight || document.documentElement.clientHeight;
				break;
			case 'windowWidth':
				obj = window.innerWidth || document.body.clientWidth || document.documentElement.clientWidth;
				break;
		}
		return obj
	};
		
	/**
	 * ajax请求
	 * @param url String 响应请求的链接
     * @param options object/function
	 * @param options.args Object 传给服务器的参数，也可以使用String，但是内容要用键值对的方式写好，参数之间用&
	 * @param options.method String 值："POST"或"GET"。默认：如果有args值就是POST，没有就GET
	 * @param options.complete Function 成功或者失败都回调的函数
	 * @param options.cache Boolean 是否缓存
	 * @param options.dataFormat String 值："JSON": 返回 JSON 数据,"String": 返回 String 数据(ps.现在返回的数据都是原始数据，不会给你转的！)
	 * 
	 * ps.输出的数据可能需要手动转换成json：var data = JSON.parse(data);
	 */
	this.ajax = function(url,options){
		var req = new XMLHttpRequest(),
			complete = options.complete||options,
			method = options.method||"POST",
			dataFormat = options.dataFormat||'JSON',
			cache = options.cache||true,
			args = options.args||'';
			
		url = url+(cache?'':((url.indexOf('?')>=0?'&':'?')+"random="+Math.random()));
		
		if(complete){
			req.onreadystatechange = function(){
				if (req.readyState==4 && req.status==200){
					//晚一些用try...catch来判断数据格式
					//var reData = req.responseText;
					//if(dataFormat=='JSON')reData = JSON.parse(reData);//JSON.stringify()
					complete(req.responseText);
					//complete.apply(null,[req.responseText]);
				}
				//else console.log('[Hquery]请求未完成或者出错:',req);
			}
		}
		req.open(method,url,true);
		var argString;
		if(args){
			req.setRequestHeader("Content-type","application/x-www-form-urlencoded");
			argString = '';
			if(typeof args=='string'){
				argString = args;
			}else{
				for(key in args){
					if(!(args[key]===undefined||args[key]===null))argString += key + '=' + encodeURIComponent(args[key]) + '&';
				}
			}
			argString = argString.replace(/(^&+)|(&+$)/g,'');//去掉前后的&符号
		}
		req.send(argString);
	};

    function ifSuppLocalStora(){
        if(!window.localStorage){
            console.error('[Hquery]浏览器不支持本地存储');
            return false;
        }else{
            return true;
        }
    }
	/**
	 * 本地存储，说明 http://www.cnblogs.com/xiaowei0705/archive/2011/04/19/2021372.html
	 * @param key String 到时候取数据要用来做参数的
	 * @param value all 存储的值，支持所有数据格式（已测试的格式包括：String、Array、Object）
     * @param time int 缓存存活的时间，单位秒
	 * 如果参数有两个，则前面是key后面是value，
	 * 如果只有一个参数，会返回一个随机key
	 */
    this.dataTimeName = '_Htime';
    /**
     * 把数据存储到localStorage
     * @param key
     * @param value
     * @param time int 单位秒，数据存活时间
     * @return boolean|int 2:存储内容超过5M。1:不支持localStorage
     * */
	this.saveData = function(key,value,time){
        if(!ifSuppLocalStora())return 1;
		if(key&&typeof value!='undefined'){
            var valueStr = JSON.stringify(value);
            if(this.strLength(valueStr)>=4500000){
                return 2;
            }
            try{
                this.delData(key);
                localStorage[key] = valueStr;
                //设置缓存时间的原理是，再在localStorage存储一个时间值，
                time = parseInt(time);
                if(time){
                    localStorage[key+this.dataTimeName] = (new Date().getTime())+time*1000;
                }
                return true;
            }catch(e){
                return 2;
            }
		}
        return 0;
	};
	this.getData = function(key,del){
        if(!ifSuppLocalStora())return;

		var timeKey = key+this.dataTimeName,
            time=localStorage[timeKey];
        if(time&&time<(new Date().getTime())){
            localStorage.removeItem(key);
            localStorage.removeItem(timeKey);
            return false;
        }

        var value = H.json(localStorage[key]);
		if(del)this.delData(key);
		return value;
	};
	this.delData = function(key){
        if(!ifSuppLocalStora())return;
		localStorage.removeItem(key);
	};

	/**
	 * 把img图片转换成base64格式，兼容ios和Android还有pc
	 * @param file String/ImgDOM 可以是图片的url，也可以是img DOM。
     * @param fn function 成功或者失败都回调的函数，结果会在回调函数的第一个参数里
	 * @param options Object 各种参数
	 * 		@object dataType string ='jpeg' 要转换的格式类型，有png，jpeg,svg+xml
	 *		@object Number quality 0-1,如果是jpeg，这个参数代表转换质量
	 *		@object Number maxWidth 转换后的图片大小，省略表示按照原图大小
	 */
    this.imgTobase64 = function(file,fn,options){
        loadImage.parseMetaData(file,function(data){
            if(data.exif){//如果图片有exif信息
                options.orientation = data.exif.get('Orientation');
            }
            options.canvas = true;
            loadImage(file,function(canvas){
                var dataType = 'image/'+(options.dataType||'jpeg');
                var quality = options.quality;
                window.c = canvas;
                console.log(window.c);
                fn(canvas.toDataURL(dataType,quality));
            },options);
        });
    };
	
	/**
	 * 加载js，css等文件
	 * @param url String 文件的url
     * @param type String 指定文件类型(省略后，此参数的位置是fn)
	 * @param fn Function/object 加载完成后执行的方法
	 * @param fn.type String 指定文件类型
	 * @param fn.complete Function 加载完成后执行的方法
	 */
	this.loadFile = function(url,type,fn){
        fn = typeof type=='string'?{complete:fn,type:type}:type;

		var filetype = fn.type || url.split('.').pop(),
			fileref;
        fn = fn.complete || fn;
		
		if(filetype == "js"){
			fileref = document.createElement('script');
			fileref.setAttribute("type","text/javascript");
			fileref.setAttribute("src",url);
		}else if(filetype == "css"){
			fileref = document.createElement('link');
			fileref.setAttribute("rel","stylesheet");
			fileref.setAttribute("type","text/css");
			fileref.setAttribute("href",url);
		}else{
			console.error('[Hquery]H.loadFile:未知的文件类型');
		}
		
		if(fn)fileref.onload = fn;
		//console.log(fileref);
		if(typeof fileref != "undefined"){
			document.getElementsByTagName("head")[0].appendChild(fileref);
		}
	};
	
	this.queryString = function(url){
		var queryString = (url||location.href).split('?');
        if(!queryString[1])
            return {};
        else
            queryString = queryString[1].split('#');
        var search = queryString[0].split('&'),
            sNum=search.length,
            obj={'_hash':queryString[1]};
		for(var i=0;i<sNum;i++){
			var arr = search[i].split('=');
			obj[arr[0]] = decodeURIComponent(arr[1]);
		}
		return obj
	};

    /**
     * 合并对象的属性（不包括原型链中的）
     * */
    this.extend = function(reWrite){
        var args = this.makeArray(arguments),i = 0,reObj = args.shift(),key,arg,W = false;
        if(typeof reWrite=='boolean'){
            W = reWrite;
            reObj = args.shift();
            if(!reObj)return false;
        }
        var aNum = args.length;
        for(;i<aNum;i++){
            arg = args[i];
            if(arg&&typeof arg=='object')
                for(key in arg){
                    if(!W&&reObj[key])
                        continue;
                    if(arg.hasOwnProperty(key))
                        reObj[key] = args[i][key];
                }
        }
        return reObj;
    };

	/**
	 * 把字符串转为json或者json转为字符串
	 * @param data allkind 支持所有数据格式，但是如果是非json格式的字符串，或者转换失败会返回本身字符串
	 * @param [tranTo] String 强制转换为什么格式，如果本身就是那种格式，会返回原数据
	 *						(主要用于不确定原数据是什么格式，但是又有可能需要转格式的时候)
	 *						值：origin ：原始字符串数据
	 *							json ：json数据
	 */
	this.json = function(data,tranTo){
		if(!data)return '';
		
		if(typeof data=='string'){//origin -> json
            data = this.trim(data);
			var firstLett = data.slice(0,2);
			if(tranTo=='origin'||firstLett.search(/\[|\{|\"|\'/i)<0){//如果这个原本是字符串的，直接返回就行了，url编码会自动解码的
                return data
			}
			try{
				var i=0;
				while(typeof data=='string'&&i<5){//不知道为啥，存进localStage里的数据取出来的时候要JSON.parse两次...
					data = JSON.parse(data);
					++i;
				}
			}
			catch(e){console.error('[Hquery error]'+e)}
			
			return data
		}else{//json -> origin
			if(tranTo=='json'){
				return data
			}
			return JSON.stringify(data)
		}
	};

    this.error = function(){
        if(navigator.userAgent.search(/MicroMessenger|ZujiBrowser/i)>=0){
            var outPutStr = '';
            for(var i= 0,aNum=arguments.length;i<aNum;i++){
                outPutStr += '\n'+H.json(arguments[i],'origin');
            }
            alert(outPutStr);
        }else{
            console.error.apply(console,arguments);
        }
    };

	/**
	 * 生成随机字符串
	 * @author hhh
	 * @param length num 生成的数是多少位的
	 * @return String 生成的随机字符串
	 */
	this.getRandChar = function(length){
		var str = null,
			strDict = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz-_~",
			max = strDict.length;
        length = length||8;

		for(var i=0;i<length;i++){
			str += strDict.charAt(Math.floor(Math.random()*max));
		}

		return str;
	};

	/**
	 * 1    Element
	 * 2	Attribute
	 * 3	Text
	 * 4	CDATA Section
	 * 5	Entity Reference
	 * 6	Entity
	 * 7	Processing Instrucion
	 * 8	Comment
	 * 9	Document
	 * 10	Document Type
	 * 11	Document Fragment
	 * 12	Notation
	 * */
	this.isNode = function(x){
		return !!(x && x.nodeType);
	};
	this.isElement = function(x){
		return x && (x.nodeType?x.nodeType==1:false);
	};

	/**
	 * 判断对象是否为数组
	 */
	this.isArray = function(x){
		return x instanceof Array;
	};

    /**
     * 把类数组的对象转为数组
     * @param arrayObj object 类数组的对象
     * @param [j=arrayObj.length] 新数组元素数，如果原对象没有length属性，则必须设置
     * @param [i=0] int 新数组从原对象中第几个元素开始。
     * */
    this.makeArray = function(arrayObj, j, i){
        return Array.prototype.slice.call(arrayObj, i || 0, j || arrayObj.length);
    };

    /**
	 * 判断对象是否为object
	 */
	this.isObject = function(x){
		return (typeof x === "function" || (typeof x === "object" && x !== null));
	};

    /**
     * 获取元素的经过计算后的所有样式
     * */
    this.getComputedStyle = function(elem,classes){
        return (window.getComputedStyle?window.getComputedStyle(elem||document.documentElement,classes||null):elem.currentStyle) || null;
    };
    /**
     * 获取浏览器各种前缀
     * */
    this.prefix = function(){
        var styles = this.getComputedStyle(document.documentElement),
            pre = (Array.prototype.slice
                .call(styles)
                .join('')
                .match(/-(moz|webkit|ms)-/) || (styles.OLink === '' && ['', 'o'])
            )[1],
            dom = ('WebKit|Moz|MS|O').match(new RegExp('(' + pre + ')', 'i'))[1];
        return {
            dom: dom,
            lowercase: pre,
            css: '-' + pre + '-',
            js: pre[0].toUpperCase() + pre.substr(1)
        };
    };

    this.inArray = function(item,array){
        var aNum = array.length;
        for(var i=0;i<aNum&&array[i]!=item;i++);
        return i<aNum;
    };

    /**
     * 与for一样，但是这个是每一次循环是由外部控制开始的，不是自动执行的
     * @param fn(i,next) function 每次循环都执行的函数
     *          i表示当前循环的编号，从0开始，相当于for循环里的i
     *          next是执行下次循环时调用的方法。
     * @param [maxI] int 当i>=maxI时，不会执行回调函数。
     * @return Queue
     * */
    this.doFor = function(fn,maxI){
        return new Queue(fn,maxI);
    };
    function Queue(fn,maxI){
        this.fn = fn;
        //this.totalTime = time;
        this.i = 0;
        this.maxI = maxI;
        this.next();
    }
    /**
     * 如果需要指定下一次执行时的i，则可以设置。
     * 为何不在fn中返回true或者其它标识时自动执行下一次循环？
     * 因为如果fn是异步函数，它返回任何东西的时候都表示它的异步还未完成，所以必须由外部调用这个才能执行下次循环
     * ps.有时候回调函数发现这次循环不需要异步，则直接return true，即可自动进入下一次循环
     * @param i int 这个i相当于for循环里的i，当这次循环的代码出现失败时（如网络请求失败了）可以重新指定下次循环时的i
     * */
    Queue.prototype = {
        next : function(i){
            i = i==undefined?this.i:i;
            if(this.maxI&&i>=this.maxI)return;

            this.i = i+1;
            var res = this.fn.call(this,i,this.next.bind(this)); //用了call，在fn中就不一定用第二个参数了。
            if(res===true)
                this.next();
        }
    };

    //获取字符串字节数(区分中英文) 中文算2个字,英文一个
    this.strLength = function(str){
        var cArr = str.match(/[^\x00-\xff]/ig);
        return str.length + (cArr == null ? 0 : cArr.length);
    };
    //计算字符串的字节数（听说这个效率比上面那个高）
    this.strByte = function(str){
        if(str){
            for(var i= 0,byte=0,len=(str.length||0); i<len; i++){
                byte += (str.charCodeAt(i)>255?2:1);
            }
            return byte;
        }else
            return 0;

    };

    /**
     * 对Date的扩展，将 Date 转化为指定格式的String
     * 月(M)、日(d)、小时(h)、分(m)、秒(s)、季度(q) 可以用 1-2 个占位符，
     * 年(y)可以用 1-4 个占位符，毫秒(S)只能用 1 个占位符(是 1-3 位的数字)
     * 例子：
     * (new Date()).Format("yyyy-MM-dd hh:mm:ss.S") ==> 2006-07-02 08:09:04.423
     * (new Date()).Format("yyyy-M-d h:m:s.S")      ==> 2006-7-2 8:9:4.18
     * @param fmt string
     * @return string
     * */
    Date.prototype.format = function(fmt){ //author: meizz
        var o = {
            "M+": this.getMonth() + 1, //月份
            "d+": this.getDate(), //日
            "h+": this.getHours(), //小时
            "m+": this.getMinutes(), //分
            "s+": this.getSeconds(), //秒
            "q+": Math.floor((this.getMonth() + 3) / 3), //季度
            "S": this.getMilliseconds() //毫秒
        };
        if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length));
        for (var k in o)
            if (new RegExp("(" + k + ")").test(fmt))
                fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
        return fmt;
    };

    /**
     * 根据时间戳来生成时间对象
     * @param time mixed 时间戳
     * @return date
     * */
    this.time = function(time){
        var t = new Date();
        if(time){
            if(typeof time=='number'||(typeof time=='string'&&time.search(/\D/)<0)){
                time = parseInt(time);
                t.setTime(time*(time<10000000000?1000:1));
            }
        }
        return t;
    };

    /**
     * 让img居中显示，一般用于img标签中的onload，注意，这个方法会让宽度超过其父级的图片强制等于父级，因为怕加载时因为还没应用上样式，原图过大导致margin-top计算错误
     * @param img img img对象
     * @param parent obj|string 如果是字符串，表示这个img对象使用$this.parents(parent)可以找到这个对象，
     *                          也可以是其它可以用$(parent)的对象，
     *                          还可以是{HW:570,HH:900}表示直接指定宽度和高度
     * @param type string 匹配的类型，width|HW表示只改横向，height|HH表示只改纵向。all表示都改
     * */
    this.imgCenter = function(img,parent,type){
        var $this = $(img),
            $par = parent?(typeof parent=='string'?$this.parents(parent):((parent.HW||parent.HH)?parent:$(parent))):$this.parent(),
            w = $this.width(),
            h = $this.height(),
            pw = ($par.HW||$par.HH)?$par.HW:$par.width(),
            ph = ($par.HW||$par.HH)?$par.HH:$par.height();
        type = type||'HH';

        if(w>pw){
            var scale = w/h;
            w = pw;
            h = w/scale;
        }
            //H.error(w,h,pw,ph);
        if((type=='all'||type=='width'||type=='HW')&&w&&pw){
            $this.css('margin-left',((pw-w)/2)+'px');
        }
        if((type=='all'||type=='height'||type=='HH')&&h&&ph){
            $this.css('margin-top',((ph-h)/2)+'px');
        }
    };

    /**
     * 用于一个页面多次请求同一个接口数据的情况，比如分页。
     * 一个基于jq，且封装好一些功能的用于发起Ajax请求的对象，封装内容有：一个方法获取下一页数据。
     * @param url string
     * @param [args] mixed
     * @param fn function
     * @param [option]=null obj
     * @param option.firstPage=1 int 第一页是几
     * @param option.pageName=page string 接口中“页数”的参数名，默认page
     * @param option.pageNum=null int 每页数据的数量，默认不设置
     * @param option.pageNumName=pagenum string 接口中“每页数据的数量”的参数名，默认pagenum
     * @param option.autoStart=true boolean 实例化对象之后自动加载第一页数据。
     * @param option.waitForFinish=true boolean 等待上一次请求完成（不论成功还是失败）之后才能再用这个对象获取数据。
     * */
    this.Ajax = function(url,args,fn,option){
        if(typeof args=='function'){
            fn = args;
            args = null;
        }

        this.baseurl = url;
        this.args = args||{};
        this.finishFn = fn;
        this.pageName = option&&option.pageName||'page';
        this.pageNum = option&&option.pageNum;
        this.pageNumName = option&&option.pageNumName||'pagenum';
        this.waitForFinish = !option||option.waitForFinish===false;
        this.nowPage = option&&option.firstPage-1 || 0;
        this.type = 0;//0等待下一次获取数据，1获取数据中。

        if(!option||option.autoStart!==false)
            this.getData(++this.nowPage);
    };
    this.Ajax.prototype = {
        /**
         * 获取指定页面的数据
         * @param page int
         * @return boolean 有可能失败的，失败的时候返回false
         * */
        getData:function(page){
            var thi = this;
            if(thi.waitForFinish&&thi.type!=0)
                return false;
            thi.type = 1;
            page = page || thi.nowPage || 1;

            var pageObj = {};
            pageObj[thi.pageName] = page;
            if(thi.pageNum)pageObj[thi.pageNumName] = thi.pageNum;

            var args = typeof thi.args=='object'?
                $.extend(pageObj,thi.args):
                (thi.pageName+'='+page+(thi.pageNum?'&'+thi.pageNumName+'='+thi.pageNum:'')+'&'+thi.args);


            $.ajax(thi.baseurl,{
                data:args,
                dataType:'json',
                success:thi.finishFn,
                complete:function(){
                    thi.type = 0;
                }
            });

            return true;
        },
        next:function(){
            this.nowPage++;
            if(!this.getData())
                this.nowPage--;
        }
    };

}();

/**
 * 专门用于足迹这个项目的函数库，需要jq支持
 * */
var ZJ = (function(){

    var o = {
        //把指定DOM的所有兄弟的 .on 去掉，指定DOM加上 .on
        selectOneBrother:function (tar) {
            if (!tar)H.error('[ZJ.selectOneBrother]无参数');
            $(tar.parentNode).children().removeClass('on');
            $(tar).addClass('on');
            return tar;
        },
        //把指定dom的高度设置为与其宽度一致
        setHeightEqWidth:function (dom) {
            dom = dom.nodeName ? dom : this;
            if (dom.nodeName) {
                var domJq = $(dom),
                    h = domJq.width();
                domJq.height(h + 'px');
                return h;
            }
            return false;
        },
        //禁止浏览器滚动的橡皮筋效果，此时如果需要在document启用 touchmove 的话，要记得加上 class：scroller
        stopScrollOver:function () {
            document.addEventListener('scroll', function (e) {
                //if(e.target==this)
                e.preventDefault();
            }, false);
            function isScroller(el) { //判断元素是否为 scroller
                return $(el).hasClass('scroller');
            }

            document.body.addEventListener('touchmove', function (ev) {
                // 在 scroller 上滑动，阻止事件冒泡，启用浏览器默认行为。
                if (isScroller(ev.target)) {
                    ev.stopPropagation();
                }
            }, false);
        },
        //时间戳转换为js日期
        timestampToDate:function (time, fmt) {
            var date;
            if (typeof time == 'number' || time.match()) {
                var timestamp = parseInt(time);
                if (timestamp && timestamp < 13413614690)
                    time = timestamp * 1000;
            }

            date = new Date(time);
            if (fmt)return date.format(fmt);
            else return date;
        },
        /**
         * input媒体处理（需要jq，$.getScript()）
         * 对视频的处理是，直接使用该input的form上传。
         * 对图片的处理是，矫正图片的方向和压缩图片，最后转为base64后调用回调函数，或自动存储到localStorage并跳转页面。
         * @param input input input对象，只处理这个input对象的文件。
         * @param [option] object 设置
         * @param option.allowMedia =['image','video'] array 允许处理的文件类型
         * @param option.imageMaxWidth =800 int 图片的最大宽度
         * @param option.imageQuality =0.8 number 图像的质量
         * @param option.autoUrl ='index.php?c=makeFactory' string 自动跳转到的url
         * @param [fn] function(base64s) 只有图片处理完成时才会调用回调函数，会把一个base64的数组传入此方法的参数中
         * */
        mediaHandle:function (input, option, fn) {
            var files = input.files,
                fNum = files.length;
            if (!(files instanceof Array)) { //因为app中选完文件之后，这个files是个类数组的对象...不是真数组
                files = H.makeArray(files);
            }
            if (!files[0])return;
            //判断文件类型
            var fileType;
            if (files[0].type) {
                fileType = files[0].type.indexOf('video') >= 0 ? 'video' : (files[0].type.indexOf('image') >= 0 ? 'image' : '');
            } else {
                var suffix = files[0].name.split('.').pop(),
                    imgSuffix = 'jpg,jpeg,png,gif,jpe',
                    videoSuffix = 'avi,m1v,m2v,m3u,m4e,movie,mp2v,mp4,mpa,mpe,mpeg,mpg,mps,wmv,3gp,mov,flv';
                fileType = imgSuffix.indexOf(suffix) >= 0 ? 'image' : (videoSuffix.indexOf(suffix) >= 0 ? 'video' : '');
            }

            if (!option || typeof option == 'function') {
                fn = option;
                option = {};
            }

            if (!option.allowMedia || H.inArray(fileType, option.allowMedia)) {
                if (fileType == 'video') {
                    input.form.submit();
                } else if (fileType == 'image') {//files[0].type.search('image')>=0
                    if (window.loadImage)
                        imgHandle();
                    else
                        $.getScript('themes/plug_in/load-image.all.min.js', imgHandle);
                }
            } else {
                alert('目前微信仅能上传视频和图片，其它功能请下载“我的足迹”app尽情体验！');
            }

            function imgHandle() {
                var urls = [];
                for (var i = 0; i < fNum; i++) {
                    try {
                        imgTobase64(files[i], function (base64) {
                            urls.push(base64);
                            if (urls.length >= fNum) { //如果图片都转完了，则保存在localStorage中（不能超过5mb），并跳转到下一页
                                if (fn)
                                    fn(urls);
                                else {
                                    H.saveData('imgUrls', urls);
                                    window.location.href = 'index.php?c=makeFactory';
                                }
                            }
                        }, {
                            maxWidth: option.imageMaxWidth || 800,
                            quality: option.imageQuality || 0.8,
                            canvas: true
                        });
                    } catch (e) {
                        H.error(e)
                    }
                }
            }

            function imgTobase64(file, fn, options) {
                loadImage.parseMetaData(file, function (data) {
                    if (data.exif) {//如果图片有exif信息
                        options.orientation = data.exif.get('Orientation');
                    }
                    loadImage(file, function (canvas) {
                        var dataType = 'image/jpeg',
                            quality = options.quality,
                            base64 = canvas.toDataURL(dataType, quality);
                        fn(base64);
                    }, options);
                });
            }
        },

        /**
         * 整理ajax数据
         * @param data json
         * @param fn
         * @param statusFn obj 处理1之外的状态的函数，这个对象里面的key都对应status的值。其中key为all的是所有出错的状态都会执行的。只要处理过出错的状态，就不会自动报错的了
         * @param mark string 因为ajax出错之后是不知道请求ajax的代码在哪的，所以请求的时候可以填写这个参数，出错了会把这个打印出来
         * */
        ajaxData:function(data, fn, statusFn, mark) {
            data = H.json(data, 'json');
            if (data.status == 1) {
                fn(data.data, data.info);
            } else if (data.status) { //处理status==1之外的
                var hasStatus = !!(statusFn && typeof statusFn[data.status] == 'function'),
                    hasAll = !!(statusFn && typeof statusFn.all == 'function');
                if (hasStatus || hasAll) {
                    hasStatus && statusFn[data.status](data);
                    hasAll && statusFn.all(data);
                } else
                    H.error(data);
            } else { //这里很可能是无法把数据从json状态转回来的时候
                var errData = mark && ('[错误标记' + mark + ']') + (data.info || data);
                if(H.alert){
                    H.alert(errData,{sure:'确定'});
                }else
                    H.error(errData);
            }
        },
        baseAjax:function(fnName,arg){
            var url = null;//把
            for (var i = 0, aNum = arg.length; i < aNum; i++){
                if (typeof arg[i] == 'function'){
                    arg[i] = (function (fn) { //封装一下回调函数，加上一个处理错误的中间件
                        return function (data){
                            ZJ.ajaxData(data, fn, false, url);
                        }
                    }(arg[i]));
                    break;
                } else if (!url && (typeof arg[i] == 'string')) {
                    url = arg[i];
                }
            }
            if (fnName == 'post')
                $.post.apply($, arg);
            else if (fnName == 'get')
                $.get.apply($, arg);
            else
                H.error('[ZJ.baseAjax]只支持jQuery的post和get，不支持' + fnName);
        },
        post:function(){
            this.baseAjax('post', H.makeArray(arguments));
        },
        get:function () {
            this.baseAjax('get', H.makeArray(arguments));
        },

        /**
         * 上传文件等比较大的东西时用的，这个能显示进度
         * 如果传入的数据是new FormData($('form')[0])，选择的form必须有这个属性：enctype="multipart/form-data"
         * @param url string
         * @param data mixed
         * @param fn function
         * @param progress function 处理上传进度的方法，回调此函数时会传入一个参数：{
         *      lengthComputable:true //是否有进度可以显示
         *      loaded:1567 //已上传字节数
         *      total:84656 //总共字节数 字节/1024/1024等于mb
         * }
         * @param statusFn object 处理服务器返回status不等于1的情况的方法，对象里的key对应status的值
         * */
        upload:function (url, data, fn, progress, statusFn){
            if (!data)
                return;
            if (!(data instanceof FormData)){
                var dataObj = {};
                if (typeof data == 'string'){
                    data = data.split('&');
                }

                var i = 0,dNum;
                if ($.isArray(data)){
                    var keyName = [];
                    for(i=0,dNum=data.length;i<dNum;i++){
                        if(typeof data[i] == 'string'){
                            keyName = data[i].split('=');
                            var key = keyName[0];
                            if (keyName[1]) {
                                if (key.search(/\[\]$/) >= 0) {//name是数组的情况下要这样做。
                                    key = key.replace(/\[\]$/, '');

                                    if (!$.isArray(dataObj[key]))
                                        dataObj[key] = [];

                                    dataObj[key].push(keyName[1]);
                                } else
                                    dataObj[key] = keyName[1];
                            }

                        }
                    }
                }else if(data.nodeName != 'FORM'&&typeof data=='object'){
                    dataObj = data;
                }

                if (data.nodeName != 'FORM') {
                    var formData = new FormData(document.createElement("form"));
                    for (var a in dataObj) {
                        if (dataObj.hasOwnProperty(a)) {
                            if ($.isArray(dataObj[a])) {
                                var arr = dataObj[a], aNum = arr.length;
                                for (i = 0; i < aNum; i++) {
                                    formData.append(a + '[]', arr[i]);
                                }
                            } else
                                formData.append(a, dataObj[a]);
                        }
                    }
                    data = formData;
                } else {
                    data = new FormData(data);
                }
            }

            $.ajax({
                url: url,  //server script to process data
                type: 'POST',
                xhr: function () {  // custom xhr
                    var myXhr = $.ajaxSettings.xhr();
                    if (myXhr.upload) { // check if upload property exists
                        myXhr.upload.addEventListener('progress', progress, false); //处理上传进度的。 for handling the progress of the upload
                    }
                    return myXhr;
                },
                //Ajax事件
                complete: function (data) {
                    //alert(H.json(data.responseText,'origin'));
                    ZJ.ajaxData(data.responseText, fn, statusFn);
                },
                // Form数据
                data: data,
                //Options to tell JQuery not to process data or worry about content-type
                cache: false,
                contentType: false,
                processData: false
            });
        }

    };


    var loadingHtml = '<jsDomName class="Hloading" style="display: none"><i class="loading"></i> <span> 正在处理，请稍候...</span></jsDomName>',
        loadingFile = 'themes/plug_in/h.loading/loading.png',
        styleCss = '<style class="loading">' +
                '.Hloading{'+
                    'display: block;'+
                    'margin: 0 auto;'+
                    'padding: 8px 0 0 0 !important;'+
                    'width: 100% !important;'+
                    'text-align: center;'+
                '}'+
                '.Hloading i.loading{'+
                    'vertical-align: text-top;'+
                    'display: inline-block;'+
                    'width: 16px;'+
                    'height: 16px;'+
                    'background: url('+loadingFile+') no-repeat;'+
                    '-webkit-animation:rota .8s linear infinite;'+
                    '-moz-animation:rota 0.8s linear infinite;'+
                    '-o-animation:rota 0.8s linear infinite;'+
                    'animation:rota 0.8s linear infinite;'+
                '}'+
            '</style>';
    o.appLoading = function(appTo,text){
        if(!appTo)return;
        var $appTo = $(appTo),
            loading = $appTo.children('.Hloading');
        if(!$appTo.length)
            return;
        $appTo.append(loading.length?loading:loadingHtml.replace(/jsDomName/g,$appTo[0].nodeName=='UL'?'li':'div'));

        loading = $appTo.children('.Hloading').show(100).find('.loading').show();
        if(text)
            loading.find('span').html(text);

        var $head = $('head');
        if(!$head.find('.loading').length)
            $head.append(styleCss);
    };
    o.removeLoading = function(appTo){
        $(appTo).children('.Hloading').remove();
    };
    //隐藏加载图标
    o.hideLoading = function(appTo,text){
        var $appTo = $(appTo),$Hloading = $appTo.children('.Hloading');
        $appTo.append($Hloading);
        $Hloading.find('i').hide(89);
        if(text)
            $Hloading.find('span').html(text);
    };

    return o;
}());